<template>
  <div>
    <h3>大文件上传</h3>
    <div class="upload_box">
      <input
        type="file"
        class="fileInput"
        ref="fileInput"
        @change="changeFile"
      />
      <div class="button_box">
        <button class="button select" type="button" @click="chooseFile">
          选择文件
        </button>
      </div>
      <div class="tip_box">只可以上传avi文件</div>
      <div class="progress_box">
        <div class="progress_bar" ref="progress_bar">
          
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import SparkMD5 from "spark-md5";
import { uploadBigFilePiece } from "@/api/upload/BigFileUploader";

export default {
  data() {
    return {
      file: null,
    };
  },
  props: ["onBeforeUpload"],
  methods: {
    chooseFile() {
      this.clearUploader();
      this.$refs.fileInput.click();
    },
    changeFile() {
      let file = this.$refs.fileInput.files[0];
      if (!file) return;
      this.file = file;


      let shouldUpload = true;
      if (this.onBeforeUpload) {
        shouldUpload = this.onBeforeUpload(file);
      }
      if (!shouldUpload) {
        return;
      }

      this.uploadFile();
    },
    async uploadFile() {
      let { hash, suffix } = await this.getFileBuffer();

      let pieces = this.cutFileToPieces(hash, suffix);

      this.uploadPieces(pieces);

    },
    getFileBuffer() {
      return new Promise((resolve) => {
        let fileReader = new FileReader();
        fileReader.readAsArrayBuffer(this.file);
        fileReader.onload = (event) => {
          let buffer = event.target.result;
          let spark = new SparkMD5.ArrayBuffer();
          spark.append(buffer);
          let hash = spark.end();
          let suffix = /\.([a-zA-Z0-9]+)$/.exec(this.file.name)[1];
          resolve({
            buffer,
            hash,
            suffix,
            filename: `${hash}.${suffix}`,
          });
        };
      });
    },
    cutFileToPieces(hash, suffix) {
      // 后端minio要求每个文件分片不能小于5m（除了最后一个分片）
      let bytesPerPiece = 1024 * 1024 * 5;
      let totalPieces = Math.ceil(this.file.size / bytesPerPiece);
      if (totalPieces > 100) {
        bytesPerPiece = this.file.size / 100;
        totalPieces = 100;
      }

      let pieces = [];
      for (let i = 0; i < totalPieces; i++) {
        /*
          i   start                 end
          0   0 * bytesPerPiece       1 * bytesPerPiece
          1   1 * bytesPerPiece       2 * bytesPerPiece
          2   2 * bytesPerPiece       3 * bytesPerPiece
          i   i * bytesPerPiece       (i + 1) * bytesPerPiece             
        */
        pieces.push({
          pieceBlob: this.file.slice(i * bytesPerPiece, (i + 1) * bytesPerPiece),
          pieceName: `${hash}_${i + 1}.${suffix}`,
          index: i,
          totalPieces,
          hash,
          suffix
        });
      }
      return pieces;
    },
    async uploadPieces(pieces) {
        for(let i = 0; i < pieces.length; i++) {
          let piece = pieces[i];
          let formData = new FormData();
          formData.append("file", piece.pieceBlob);
          formData.append("hash", piece.hash);
          formData.append("pieceIndex", piece.index);
          formData.append("totalPieces", piece.totalPieces);
          formData.append("suffix", piece.suffix);
          // 这里使用await，是为了用微任务做单线程上传文件
          let resultVo = await uploadBigFilePiece(formData);
          if(i == pieces.length - 1) {
              this.$message.success(resultVo.msg);
              this.$refs.progress_bar.style.width = "100%";
          } else {
              this.$refs.progress_bar.style.width = i / piece.totalPieces * 100 + "%";
          }
        }
    },
    clearUploader() {
      // 重置file input
      this.$refs.fileInput.value = null;
      this.file = null;
      this.$refs.progress_bar.style.width = "0%";
    }
  },
};
</script>

<style scoped>
.upload_box {
  width: 400px;
  height: 150px;
  border: #ccc dashed 1px;
  margin: auto;
  font-size: 12px;
}
.fileInput {
  display: none;
}
.button.select {
  margin: 5px;
  padding: 5px;
  border: #ccc outset 1px;
  border-radius: 5px;
  background-color: #00b9f1;
}
.button.select:active {
  border-right-style: inset;
  border-bottom-style: inset;
}
.progress_box {
  width: 100%;
  height: 5px;
  background-color: #ccc;
}
.progress_bar {
  width: 0%;
  height: 5px;
  background-color: #00b9f1;
}
</style>
